﻿using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Diagnostics;

namespace GoodStuff.Web.Controls
{
    /// <summary>
    /// Display the current version of the running assembly, when running in debug-build
    /// </summary>
    /// <remarks>
    /// This only works when the assembly version is in the format [assembly: AssemblyVersion("0.9.*")]
    /// The * indicates that the last two parts are automatically generated. This control depends on
    /// that automatic generated information. When using AssemblyFileVersion, the date is taken from the
    /// last write time of the DLL since FileVersion has no * construct.
    /// </remarks>
    public class VersionLabel : Control
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public VersionLabel()
        {
            //some default values
            this.DebugBuildOnly = false;
            this.VersionType = VersionInformation.AssemblyVersion;
        }

        /// <summary>
        /// Get/Sets if this control should be rendered in debug builds only. Default is false
        /// </summary>
        public bool DebugBuildOnly { get; set; }

        /// <summary>
        /// The type of version information the control has to render
        /// </summary>
        public VersionInformation VersionType { get; set; }        

        /// <summary>
        /// Renders the current version of the running assembly, when running in debug-build
        /// </summary>
        /// <param name="writer"></param>
        protected override void Render(HtmlTextWriter writer)
        {
#if DEBUG
            if (this.Page != null)
            {
                Type t = this.Page.GetType();
                while (t.Namespace == "ASP" && t.BaseType != null)
                {
                    t = t.BaseType;
                }

                Assembly assembly = t.Assembly;
                writer.Write(VersionLabelText.GetDisplayText(assembly, this.VersionType));
            }
            else
            {
                Assembly assembly = Assembly.GetExecutingAssembly();
                writer.Write(VersionLabelText.GetDisplayText(assembly, this.VersionType));
            }
            
#endif
        }       
    }

    /// <summary>
        /// Type of version to use.
        /// </summary>
        public enum VersionInformation
        {
            /// <summary>
            /// Use the AssemblyVersion
            /// </summary>
            AssemblyVersion,
            
            /// <summary>
            /// Use the AssemblyFileVersion
            /// </summary>
            AssemblyFileVersion
        }

    /// <summary>
    /// Helper class which uses the VerionLabel class to return a displaytext for the given assembly
    /// </summary>
    public static class VersionLabelText
    {
        /// <summary>
        /// Constructs a string with name and version information of the current running (main)assembly
        /// </summary>
        /// <param name="assembly"></param>
        /// <param name="versionType"></param>
        /// <returns></returns>
        public static string GetDisplayText(Assembly assembly, VersionInformation versionType)
        {
            switch (versionType)
            {
                case VersionInformation.AssemblyVersion:
                    {
                        AssemblyName builtInfo = assembly.GetName();
                        DateTime d = CalculateDateTime(builtInfo.Version.Build, builtInfo.Version.Revision);
                        return GetDisplayText(TryGetAssemblyTitle(assembly), builtInfo.Version.Major, builtInfo.Version.Minor, builtInfo.Version.Build, d);
                    }
                case VersionInformation.AssemblyFileVersion:
                default:
                    {
                        FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
                        System.IO.FileInfo fileInfo = new System.IO.FileInfo(assembly.Location);
                        DateTime lastModified = fileInfo.LastWriteTime;
                        return GetDisplayText(TryGetAssemblyTitle(assembly), fvi.ProductMajorPart, fvi.ProductMinorPart, fvi.ProductBuildPart, lastModified);
                    }
            }
        }

        /// <summary>
        /// Tries to get the title based on the AssemblyTitleAttribute. If not available, it uses the name of the assembly
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        private static string TryGetAssemblyTitle(Assembly assembly)
        {
            AssemblyTitleAttribute titleAttribute = assembly.GetAttribute<AssemblyTitleAttribute>();
            if (titleAttribute != null && !string.IsNullOrEmpty(titleAttribute.Title))
            {
                return titleAttribute.Title;
            }
            else
            {

                AssemblyName builtInfo = assembly.GetName();
                return builtInfo.Name;
            }
        }

        /// <summary>
        /// Formats the different information to a display text.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="major"></param>
        /// <param name="minor"></param>
        /// <param name="build"></param>
        /// <param name="builded"></param>
        /// <returns></returns>
        private static string GetDisplayText(string name, int major, int minor, int build, DateTime builded)
        {
            return string.Format("{0} version {1}.{2} build {3} ({4:G})",
                        name,
                        major,
                        minor,
                        build,
                        builded);
        }

        /// <summary>
        /// Calculates the builddate (and time)
        /// </summary>
        /// <param name="build"></param>
        /// <param name="revision"></param>
        /// <returns></returns>
        private static DateTime CalculateDateTime(int build, int revision)
        {
            DateTime d = new DateTime(2000, 1, 1, 0, 0, 0);
            d += TimeSpan.FromDays(build) + TimeSpan.FromSeconds(revision * 2);
            if (TimeZone.IsDaylightSavingTime(DateTime.Now, TimeZone.CurrentTimeZone.GetDaylightChanges(DateTime.Now.Year)))
            {
                d = d.AddHours(1);
            }
            return d;
        }
    }

}
